import goods from './data.json'
import UIElement from './doms'
import './cat.scss'

interface I_Goods {
  id: number | string
  title: string
  pic: string | URL
  desc: string
  sellNumber: number
  price: number
  favorRate: number
}

/** 包装商品数据 */
class UIGoods {
  data: I_Goods | undefined

  get totalPrise() {
    return this.choose * this.data!.price
  }

  get isChoose() {
    return this.choose > 0
  }

  choose = 0

  /**
   * @param  {I_Goods} g 单件商品数据
   */
  constructor(g: I_Goods) {
    g = { ...g } // 克隆对象
    Object.freeze(g) // 冻结对象
    Object.defineProperty(this, 'data', {
      get() {
        return g
      },
      set() {
        throw Error('UIGoods 的 data 属性是不可修改的')
      }
    })
    let choose: number = 0
    Object.defineProperty(this, 'choose', {
      get() {
        return choose
      },
      set(val) {
        choose = val
      }
    })
    Object.seal(this) // 密封属性
  }
}

/** 页面数据 */
class UIData {
  /** 商品数据 */
  uiGoods: UIGoods[] = []

  /** 商品总价 */
  get totalPrise(): number {
    return this.uiGoods.reduce((pre, cur) => pre + cur.totalPrise, 0)
  }

  /** 商品总数 */
  get totalChooseNumber(): number {
    return this.uiGoods.reduce((pre, cur) => pre + cur.choose, 0)
  }

  /** 购物车内是否有商品 */
  get hasGoodsInCat(): boolean {
    return !!this.totalChooseNumber
  }

  /** 是否超过起送门槛 */
  get isCrossDeliveryThreshold(): boolean {
    return this.totalPrise >= this.deliveryThreshold
  }

  /**
   * @param deliveryThreshold 起送门槛, 默认 30
   * @param deliveryPrise 配送费, 默认5
   */
  constructor(public deliveryThreshold = 30, public deliveryPrise = 5) {
    this.uiGoods = (goods as I_Goods[]).map(item => new UIGoods(item))
    // Object.freeze(this)
  }

  isChoose(index: number) {
    return this.uiGoods[index].isChoose
  }

  increase(index: number) {
    this.uiGoods[index].choose++
  }

  decrease(index: number) {
    this.uiGoods[index].choose--
  }
}

/** 页面对象 */
class UI {
  uiData

  get doms() {
    return {
      goodsContainer: document.querySelector<HTMLDivElement>(
        '.goods-container .goods-list'
      )!,
      footerPay: document.querySelector<HTMLDivElement>('.goods-footer-pay')!,
      cat: document.querySelector<HTMLDivElement>('.goods-footer-icon')!,
      devicePrise: document.querySelector<HTMLDivElement>(
        '.goods-footer .prise-tip'
      )!,
      footerPayInSpan: document.querySelector<HTMLSpanElement>(
        '.goods-footer-pay span'
      )!,
      totalPrise: document.querySelector<HTMLDivElement>(
        '.goods-footer .prise-number'
      )!,
      badge: document.querySelector<HTMLSpanElement>(
        '.goods-footer .footer-badge'
      )!
    }
  }

  jumpTarget = { x: 0, y: 0 }

  constructor() {
    this.uiData = new UIData()
    const rect = this.doms.cat?.getBoundingClientRect()!
    Object.defineProperty(this, 'jumpTarget', {
      get() {
        if (!rect) return { x: 0, y: 0 }
        return {
          x: ~~(rect.left + rect.width / 2),
          y: ~~(rect.top + rect.height / 5)
        }
      },
      set() {
        throw Error('jumpTarget 不可以修改')
      }
    })

    this.info()
  }

  info() {
    this.__innerHTML()
    this.updateFooter()
    this.listenEvent()
  }

  __innerHTML() {
    const html = this.uiData.uiGoods
      .map((item, index) => {
        return `
        <div class="goods-item">
          <img src="${item.data?.pic}" alt="${item.data?.title}" class="goods-pic">
          <div class="goods-info">
            <h2 class="goods-title">${item.data?.title}</h2>
            <p class="goods-desc">${item.data?.desc}</p>
            <p class="goods-cell">
              <span>月售${item.data?.sellNumber}</span>
              <span>好评${item.data?.favorRate}%</span>
            </p>
            <div class="goods-confirm">
              <p class="goods-prise">
                <span class="goods-prise-unit">¥</span>
                <b>${item.data?.price}</b>
              </p>
              <div class="goods-btns">
                <i class="i-jianhao" data-index="${index}">-</i>
                <span>${item.choose}</span>
                <i class="i-jiahao" data-index="${index}">+</i>
              </div>
            </div>
          </div>
        </div>
        `
      })
      .join('')
    this.doms.goodsContainer.innerHTML = html
  }

  updateFooter() {
    const total = this.uiData.totalPrise
    // 设置配送费
    this.doms.devicePrise.textContent = this.uiData.deliveryPrise.toFixed(2)
    // 设置总价
    if (this.uiData.isCrossDeliveryThreshold) {
      this.doms.footerPay.classList.add('active')
    } else {
      this.doms.footerPay.classList.remove('active')
      const dis = ~~(this.uiData.deliveryThreshold - total)
      this.doms.footerPayInSpan.textContent = `还差 ¥${dis}元起送`
    }
    this.doms.totalPrise.textContent = total.toFixed(2)
    this.doms.cat.classList[this.uiData.hasGoodsInCat ? 'add' : 'remove'](
      'active'
    )
    this.doms.badge.textContent = this.uiData.totalChooseNumber.toFixed(0)
  }

  listenEvent() {
    this.doms.cat.addEventListener('animationend', () => {
      this.doms.cat.classList.remove('animate')
    })
  }

  updateGoodsItem(index: number) {
    const goodDom = this.doms.goodsContainer.children[index]
    goodDom.classList[this.uiData.isChoose(index) ? 'add' : 'remove']('active')
    const span = goodDom.querySelector<HTMLElement>('.goods-btns span')!
    span.textContent = this.uiData.uiGoods[index].choose.toFixed(0)
  }

  /**
   * 抛物线跳跃元素
   * @param index
   */
  jump(index: number) {
    const rect = this.doms.goodsContainer.children[index]
      .querySelector('.i-jiahao')!
      .getBoundingClientRect()
    const start = {
      x: ~~(rect.left - rect.width / 2),
      y: ~~(rect.top - rect.height / 2)
    }
    const div = document.createElement('div')
    const span = document.createElement('span')
    div.className = 'add-to-cat'
    span.className = 'add-to-cat-icon'
    span.textContent = '+'
    div.style.transform = `translateX(${start.x}px)`
    span.style.transform = `translateY(${start.y}px)`
    div.appendChild(span)
    document.body.append(div)
    let time: number = Date.now()
    const animationFrame = () => {
      div.style.transform = `translateX(${this.jumpTarget.x}px)`
      span.style.transform = `translateY(${this.jumpTarget.y}px)`

      if (Date.now() - time < 5000) {
        window.requestAnimationFrame(animationFrame)
      }
    }
    requestAnimationFrame(animationFrame)

    span.addEventListener(
      'transitionend',
      () => {
        div.remove()
        this.carAnimate()
      },
      { once: true }
    )
  }

  carAnimate() {
    this.doms.cat.classList.add('animate')
  }

  increase(index: number) {
    this.uiData.increase(index)
    this.updateGoodsItem(index)
    this.updateFooter()
    this.jump(index)
  }

  decrease(index: number) {
    this.uiData.decrease(index)
    this.updateGoodsItem(index)
    this.updateFooter()
  }
}

const app = document.getElementById('app')!

app.appendChild(UIElement)
const ui = new UI()

ui.doms.goodsContainer.addEventListener('click', e => {
  const classList = (e.target as HTMLElement).classList
  const { index = '0' } = (e.target as HTMLElement).dataset as Record<
    string,
    string
  >
  if (classList.contains('i-jianhao')) {
    ui.decrease(~~index)
  } else if (classList.contains('i-jiahao')) {
    ui.increase(~~index)
  }
})
